package net.mingsoft.report_data.biz.impl;

import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import net.mingsoft.basic.util.ApplicationContextUtil;
import net.mingsoft.basic.util.DateUtil;
import net.mingsoft.cms.biz.IContentBiz;
import net.mingsoft.cms.entity.ContentEntity;
import net.mingsoft.cms.util.ContentCodeUtil;
import net.mingsoft.report_data.action.q.HtmlQuery;
import net.mingsoft.report_data.action.r.ReportTypeResponse;
import net.mingsoft.report_data.biz.*;
import net.mingsoft.report_data.config.ReportDetailProperty;
import net.mingsoft.report_data.constant.ZkDataConstant;
import net.mingsoft.report_data.dao.ZkDataDao;
import net.mingsoft.report_data.entity.ZkDataMetricEntity;
import net.mingsoft.report_data.entity.ZkDataReportConfigEntity;
import net.mingsoft.report_data.enums.PeriodEnum;
import net.mingsoft.report_data.enums.ReportPeriodType;
import net.mingsoft.report_data.enums.ReportType;
import net.mingsoft.report_data.util.ArabNumber;
import net.mingsoft.report_data.util.ReportReflectUtil;
import net.mingsoft.report_data.util.ReportFtlRender;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author gengfa
 * @date 22/03/09
 */
@Service
@Slf4j
public class ZkDataBizImpl implements ZkDataBiz {

    @Resource
    IContentBiz contentBiz;

    @Resource
    ZkDataDao dataDao;

    @Resource
    ZkDataReportPopulationAnalysisBiz populationAnalysisBiz;

    @Resource
    ZkDataCityOdAnalysisBiz cityOdAnalysisBiz;

    @Resource
    ZkDataCityLiveBiz cityLiveBiz;
    @Resource
    ZkDataEnterpriseActivityOfEconomyBiz enterpriseActivityOfEconomyBiz;
    @Resource
    ZkDataPeoplesLiveHoodBiz peoplesLiveHoodBiz;

    @Resource
    ZkDataOnVacationFlowPopulationBiz onVacationFlowPopulationBiz;

    @Resource
    ZkDataFocusAreaBiz focusAreaBiz;

    @Resource
    ZkDataSuspiciousBoatBiz suspiciousBoatBiz;

    @Resource
    ZkDataSgSqBiz sgSqBiz;

    @Resource
    ReportDetailProperty property;

    @Resource
    FreeMarkerConfigurer configurer;

    private static final String REQUEST_STATUS = "5";

    private static final Map<String, Function<Map<String, Object>, Map<String, Object>>> METRIC_MAP = new HashMap<>(16);

    @PostConstruct
    public void initMap() {
        // 动态人口报告分析
        METRIC_MAP.put("dp_permanent", m -> populationAnalysisBiz.getPermanentPopulationData(PeriodEnum.WEEK, m));
        METRIC_MAP.put("dp_flow", m -> populationAnalysisBiz.getFlowPopulation(m));
        METRIC_MAP.put("dp_foreign", m -> populationAnalysisBiz.getForeignPopulationByLocalCity(populationAnalysisBiz.getForeignPopulation(m), m));
        METRIC_MAP.put("dp_area", m -> populationAnalysisBiz.getPopulationDistributed(m));
        METRIC_MAP.put("dp_smuggler_od", m -> {
            populationAnalysisBiz.getSmugglerOdTimesData(m);
            populationAnalysisBiz.getSmugglerOdPeriodData(m);
            populationAnalysisBiz.getSmugglerOdDistanceData(m);
            populationAnalysisBiz.getSmugglerOdArriveAreaData(m);
            populationAnalysisBiz.getSmugglerWorkHomeData(m);
            return m;
        });
        METRIC_MAP.put("dp_coastal", m -> populationAnalysisBiz.getCoastalActiveAreaData(populationAnalysisBiz.getCoastalWorkHomePopulationData(m), m));
        METRIC_MAP.put("dp_city_migrate", m -> populationAnalysisBiz.getCityMigrationData(m));

        METRIC_MAP.put("dp_realtime_user", m -> populationAnalysisBiz.getRealTimeUser(m));
        METRIC_MAP.put("dp_in_out_island", m -> populationAnalysisBiz.getInOutIslandUser(m));
        METRIC_MAP.put("dp_pop_structure", m -> populationAnalysisBiz.getPopulationStructure(m));
        METRIC_MAP.put("dp_area_job_structure", m -> populationAnalysisBiz.getAreaJobStructure(m));

        // 城市人口通勤分析
        METRIC_MAP.put("co_summary", m -> {
            cityOdAnalysisBiz.getOdAverageData(m);
            return cityOdAnalysisBiz.getDistanceFeatureData(m);
        });
        METRIC_MAP.put("co_time", m -> {
            cityOdAnalysisBiz.getOdPeriodFeatureData(m);
            return cityOdAnalysisBiz.getOdCostTimeFeatureData(m);
        });
        METRIC_MAP.put("co_work_home", m -> cityOdAnalysisBiz.getOdFeatureWorkHomeData(m));
        METRIC_MAP.put("co_type", m -> cityOdAnalysisBiz.getTravelModeData(m));

        // 城市活力
        METRIC_MAP.put("cl_heat_map", m -> cityLiveBiz.getHeatMap(m));
        METRIC_MAP.put("cl_area_rank", m -> cityLiveBiz.getAreaRank(m));
        METRIC_MAP.put("pl_weibo_sign", m -> cityLiveBiz.getWeiboSignedUser(m));

        // 企业经济行为分析
        METRIC_MAP.put("eae_illegal_sale", m -> enterpriseActivityOfEconomyBiz.getFreeTaxIllegalSale(m));
        METRIC_MAP.put("eae_internet_sale", m -> enterpriseActivityOfEconomyBiz.getFreeTaxInternetSale(m));
        METRIC_MAP.put("eae_smuggle_area", m -> enterpriseActivityOfEconomyBiz.getFreeTaxSmugglingArea(m));
        METRIC_MAP.put("eae_user_flow_economy", m -> sgSqBiz.getPopulationFlowAndEconomicMetric(m));

        // 社会民生
        METRIC_MAP.put("pl_hot_event", m -> peoplesLiveHoodBiz.getHotEvents(m));
        METRIC_MAP.put("pl_public_opinion", m -> {
            peoplesLiveHoodBiz.getPublicOpinionDevelopment(m);
            peoplesLiveHoodBiz.getMediaReportTrending(m);
            return m;
        });

        METRIC_MAP.put("pl_important_media", m -> peoplesLiveHoodBiz.getPopMediaWithNegativeInfo(m));
        METRIC_MAP.put("pl_anti_smuggle", m -> peoplesLiveHoodBiz.getInIslandAntiSmugglerReportCount(m));
        METRIC_MAP.put("pl_specific_employee", m -> peoplesLiveHoodBiz.getSpecificEmployeeDto(m));

        // 走私上岸与隐蔽码头分析报告
        METRIC_MAP.put("smuggle_dock", m -> suspiciousBoatBiz.getHiddenDock(m));

        // 节假日人口流动分析报告
        METRIC_MAP.put("ov_user_peak", m -> onVacationFlowPopulationBiz.getUserPeak(m));
        METRIC_MAP.put("ov_out_flow_trend", m -> onVacationFlowPopulationBiz.getOutProvinceFlowUserTrend(m));
        METRIC_MAP.put("ov_abroad_user_trend", m -> onVacationFlowPopulationBiz.getAbroadFlowUserTrend(m));
        METRIC_MAP.put("ov_tourist_trend", m -> onVacationFlowPopulationBiz.getTouristTrend(m));
        METRIC_MAP.put("ov_out_migrate_trend", m -> onVacationFlowPopulationBiz.getOutProvinceUserMigrateTrend(m));
        METRIC_MAP.put("ov_abroad_migrate_trend", m -> onVacationFlowPopulationBiz.getAbroadUserMigrateTrend(m));

        // 关注区域
        METRIC_MAP.put("fa_summary", m -> focusAreaBiz.getSummary(m));
        METRIC_MAP.put("fa_scene_user_top5", m -> focusAreaBiz.getSceneUserTop5(m));
        METRIC_MAP.put("fa_traffic_top5", m -> focusAreaBiz.getTrafficUserTop5(m));
        METRIC_MAP.put("fa_other_user_top5", m -> focusAreaBiz.getOtherAreaUserTop5(m));
        METRIC_MAP.put("fa_area_chan_situ", m -> focusAreaBiz.getAreaChangingState(m));
    }

    @Override
    public String genReport(PeriodEnum periodEnum, ReportType reportType) {
        String html = "";

        /*if (reportType == ReportType.DYNAMIC_POPULATION) {
            html = populationAnalysisBiz.generate(periodEnum);
        } else if (reportType == ReportType.CITY_OD) {
            html = cityOdAnalysisBiz.generate(periodEnum);
        }

        // 保存报告到对应栏目
        if (StringUtils.isNotBlank(html)) {
            saveReport(html, reportType);
        }*/

        return html;
    }

    @Override
    public void autoGenerateReports(ReportPeriodType periodType, List<ZkDataReportConfigEntity> reports) {
        log.info("[AUTO GENERATE] generating reports");
        reports.forEach(report -> {
            Object object = ApplicationContextUtil.getObject(report.getReportType());
            Object result = ReportReflectUtil.generate(object, periodType);
            log.info("generated report {}", report.getReportType());
            saveReport((String) result, report.getReportType(), ReportPeriodType.getTitlePrefix(periodType));
        });
        log.info("[AUTO GENERATE] finished");
    }

    @Override
    public String genReport(HtmlQuery query) {
        List<ZkDataMetricEntity> entities = dataDao.selectMetricByIds(query.getIds());
        StringBuilder html = new StringBuilder();
        HashMap<String, Object> map = new HashMap<>();
        map.put("title", query.getTitle());
        map.put("pubTime", query.getPubTime());
        map.put("author", query.getAuthor());
        map.put("readCount", query.getReadCount());

        DateTime now = DateTime.now();
        // default period type: last 7 days (include current day)
        /*map.put("beginDate", now.minusDays(6).toString(ZkDataConstant.DATETIME_FORMATTER_STRING));
        map.put("endDate", now.plusDays(1).toString(ZkDataConstant.DATETIME_FORMATTER_STRING));*/

        // 22/06/13 modify default period type: last 7 days (exclude current day)
        map.put("beginDate", now.minusDays(7).toString(ZkDataConstant.DATETIME_FORMATTER_STRING));
        map.put("endDate", now.toString(ZkDataConstant.DATETIME_FORMATTER_STRING));

        String headHtml = ReportFtlRender.render(configurer, "report/metric/metric-head.ftl", map);
        String footHtml = ReportFtlRender.render(configurer, "report/metric/metric-footer.ftl", map);
        html.append(headHtml);
        for (int i = 0; i < entities.size(); i++) {
            html.append(generateFromFtl(i+1, entities.get(i).getMetricFtl(), map));
        }
        html.append(footHtml);
        return html.toString();
    }

    private String generateFromFtl(int serialNumber, String template, Map<String, Object> map) {
        map.put("serialNumber", ArabNumber.toChinese(serialNumber));
        METRIC_MAP.get(template).apply(map);
        return ReportFtlRender.render(configurer, "report/metric/" + template + ".ftl", map);
    }

    @Override
    public boolean saveReport(String reportHtml, ReportType reportType) {
        Date now = new Date();
        ContentEntity content = new ContentEntity();
        // 发布申请状态
        content.setContentTitle(reportType.getContentTitle());
        content.setCategoryId(getCategoryId(reportType));
        content.setContentType(property.getType());
        content.setContentDisplay("0");
        content.setContentAuthor(property.getAuthor());
        content.setContentSource("");
        content.setContentSort(0);
        content.setContentImg(property.getCover());
        content.setContentDescription("");
        content.setContentKeyword("");
        content.setLocalUpload("[]");
        content.setFileId("");
        content.setContentDetails(reportHtml);
        content.setContentDatetime(now);
        content.setCreateDate(now);
        content.setCreateBy(property.getAdmin());
        content.setUpdateDate(now);
        content.setSendApply(REQUEST_STATUS);
        return contentBiz.save(content);
    }

    @Override
    public boolean saveReport(String reportHtml, String reportType, String reportPeriodSuffix) {
        Date now = new Date();
        ContentEntity content = new ContentEntity();
        ReportType reportT = ReportType.getReport(reportType);
        List<ZkDataMetricEntity> zkDataMetricEntities = dataDao.selectMetricByReportType(reportType);
        long[] metrics = zkDataMetricEntities.stream().mapToLong(ZkDataMetricEntity::getId).toArray();
        // 发布申请状态
        content.setCode(ContentCodeUtil.getCode());
        content.setContentTitle(DateUtil.nowZhCN() + reportPeriodSuffix + reportT.getContentTitle());
        content.setCategoryId(getCategoryId(reportT));
        content.setContentType(property.getType());
        content.setContentDisplay("0");
        content.setContentAuthor(property.getAuthor());
        content.setContentSource("");
        content.setContentSort(0);
        content.setContentImg(property.getCover());
        content.setContentDescription("");
        content.setContentKeyword("");
        content.setLocalUpload("[]");
        content.setFileId("");
        content.setContentDetails(reportHtml);
        content.setContentDatetime(now);
        content.setCreateDate(now);
        content.setCreateBy(property.getAdmin());
        content.setUpdateDate(now);
        content.setSendApply(REQUEST_STATUS);
        content.setGenerateType(3);
        content.setMetricIds(JSON.toJSONString(metrics));
        return contentBiz.save(content);
    }

    @Override
    public boolean saveReportManu(String date, String reportHtml, String reportType, String reportPeriodSuffix) {
        ContentEntity content = new ContentEntity();
        ReportType reportT = ReportType.getReport(reportType);
        List<ZkDataMetricEntity> zkDataMetricEntities = dataDao.selectMetricByReportType(reportType);
        long[] metrics = zkDataMetricEntities.stream().mapToLong(ZkDataMetricEntity::getId).toArray();
        // 发布申请状态
        content.setCode(ContentCodeUtil.getCode());
        content.setContentTitle(DateUtil.nowZhCNByDateString(date) + reportPeriodSuffix + reportT.getContentTitle());
        content.setCategoryId(getCategoryId(reportT));
        content.setContentType(property.getType());
        content.setContentDisplay("0");
        content.setContentAuthor(property.getAuthor());
        content.setContentSource("");
        content.setContentSort(0);
        content.setContentImg(property.getCover());
        content.setContentDescription("");
        content.setContentKeyword("");
        content.setLocalUpload("[]");
        content.setFileId("");
        content.setContentDetails(reportHtml);
        content.setContentDatetime(StringToDate(date));
        content.setCreateDate(StringToDate(date));
        content.setCreateBy(property.getAdmin());
        content.setUpdateDate(StringToDate(date));
        content.setSendApply(REQUEST_STATUS);
        content.setGenerateType(3);
        content.setMetricIds(JSON.toJSONString(metrics));
        return contentBiz.save(content);
    }

    public Date StringToDate(String stringDate){
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        try {
            return sdf.parse(stringDate);
        } catch (ParseException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return new Date();
    }

    @Override
    public List<ReportTypeResponse> getReportTypes() {
        List<ReportTypeResponse> responses = new ArrayList<>();
        List<ZkDataReportConfigEntity> entities = dataDao.selectReportConfig(false);
        entities.forEach(it -> {
            responses.add(new ReportTypeResponse().setName(it.getReportName()).setType(it.getReportType()));
        });
        return responses.stream()
                .filter(o -> !StringUtils.equals("CITY_PLAN_SMART_SELECT", o.getType()))
                .collect(Collectors.toList());
    }

    @Override
    public List<ZkDataMetricEntity> getReportMetric(String type) {
        return dataDao.selectMetricByReportType(type);
    }

    @Override
    public List<ZkDataReportConfigEntity> getReports() {
        return dataDao.selectReportConfig(true);
    }

    private String getCategoryId(ReportType reportType) {
        if (reportType == ReportType.CITY_OD) {
            return property.getColOd();
        } else {
            return property.getColPopulation();
        }
    }
}
